home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD ROM Paradise Collection 4
/
CD ROM Paradise Collection 4 1995 Nov.iso
/
edit
/
aaem95ma.zip
/
MACROS.CC
< prev
next >
Wrap
C/C++ Source or Header
|
1995-02-10
|
34KB
|
682 lines
/* This is file MACROS.CC */
#include "em.h"
/*-----*/
/* int ____; long int bp(){__SR(); ____=_bp; __SR(); return ____;}
void BP(){int i=0; long*p=(long int*)bp();
while(p) {i++; p=(long int*)*p;} for(;i>0;i--) fprintf(debug," ");} */
/*-----*/
char*CMN="current_macro",keychar,keyseqc[65]; val keyseq(keyseqc,_keyseq);
macro*Record=0; var*globvars=0; int nglobvars=0; val*refaddr(val L);
mark err(0,0); val comment("/*",2),tnemmoc("*/",2);
/*-----*/
KF(obey){val f; switch(T1.n){int i;
case 0: if(record) MOAN("can't call macro which is being recorded"); T1.m=Macro;
case _macro: (*T1.m)(N); return;
default: MOAN("obey() with bad arg or no arg");
case _keyseq: f=T1.keyseq(); if(f.n==_bad) MOAN(f.s);
if(f.n!=_macro) MOAN("error in obey(): this key is not bound to a macro");
(*f.m)(N);}}
/*-----*/
KF(macromenu){macro*M; M=macro_menu(); if(!M) MOAN("aborted"); (*M)();};
/*-----*/
KF(namemacro) {int i; macro*M; val nam; if(T2.n!=_macro)
T2=*getk(T2,"type key sequence bound to the macro (ctrlX E for current macro)");
T1.getifn(T1t,"name to bind to the macro?");
for(i=0;i<T1.n;i++) if(!isalnum(T1.s[i])) MOAN("name must be all alphanumeric");
if(T1.n) {if(!isalpha(T1.s[0])) MOAN("name must start with a letter");
nam=named(T1); if(nam.n!=_unidfname) {
pr(CW,"'%s' is already a %t",T1.s,nam.typ()); MOAN(CW);}}
if(!play) if(basemi.rec) {setrek(T1,T1.copy()); setrek(T2,keyseq.copy());}
if(T2.n==_subr?T2.f==&obey:0) M=Macro; /* as 'ctrlX E' calls current macro */
else if(T2.n==_macro) M=T2.m;
else {pr(CW,"%s is not bound to a macro",&keyseq); MOAN(CW);}
if(M->name) if(M->name!=CMN) {
pr(CW,"macro already named '%s': shall I rename it?",M->name);
if(!yesno(CW)) MOAN("user abort"); delete M->name;}
M->name=copyof(T1);}
/*-----*/
KF(beginmacro) {macro*m; if(play) return; if(record) MOAN("already recording");
Display="start macro"; if(Macro) if(Macro->name==CMN) Macro->name=0;
if(!Macro?:Macro->bound.n?:Macro->name!=0)Macro=new macro();else Macro->empty();
basemi.rec=0; record=Macro; thisstep.f=&_idle; laststep.del(); laststep.clear();
if(T2.n==_keyseq) bindkeymacro(val(1,0),T2);
if(T1.n>0) namemacro(val(1,0),T1,val(record));
else {for(m=macros;m;m=m->next) if(m->name==CMN)
if(m->bound.n) m->name=0; else delete m; record->name=CMN;}}
/*-----*/
KF(endmacro) {if(play) return;
Display="end macro"; if(!record) MOAN("not recording"); basemi.rec=0;
record->tidy(); record=0; thisstep.f=&_idle; laststep.del(); laststep.clear();}
/*-----*/
KF(repeat) {if(play) {mi->rec=0; (*mi->prevstep)(N.i); return;}
laststep(N.i); if(record)(*record)+=new macstep(kf(&repeat),N); rept=1;}
/*-----*/
KF(prmacro){Macro->print();}
/*-----*/
KF(prmacros) {prvars(globvars); macro*M; for(M=macros;M;M=M->next) M->print();}
/*-----*/
macro*macros=0;
/*-----*/
macstep laststep,thisstep;
val specialchars("\"\\^`",4); /* chars that need \ before as selves in string */
char*rsvword[]={0};
char*Keysort[]={"magicstring","string","unbound","subroutine","macro","char",
"keyarray","buffer","int","keysequence","reservedword","unidentifiedname",
"call","bad","uncheckedsubr","function","functionwithinfo","reference","type",
"float","label",0};
char**keysort=Keysort+2;
/*-----*/
#define Bad(s) ({err=*this; *this=x; return val(s,_bad);})
#define badq(s) if((s).n==_bad) return (s)
#define Badq(s,delendum) if((s).n==_bad) {(delendum).del(); return (s);}
/*-----*//* skip whites and comments */
void mark::Skip(){int n; char*s;
A: n=r->n; s=r->s;
C: if(c<n) {if(s[c]==' '?:s[c]==9) {c++; goto C;}}
else if(r->next) {r=r->next; c=0; goto A;} else return;
if(c>n-2 ?: s[c]!='/' ?: s[c+1]!='*') return; c+=2;
B: if(c<=n-2) if(s[c]=='*') if(s[c+1]=='/') {c+=2; goto A;}
if(c<n) c++; else if(r->next) {r=r->next;c=0;} else MOAN("missing */"); goto B;}
/*-----*/
int mark::thisch(char C){Skip(); int i; if(i=(**this==C)) ++*this; return i;};
/*-----*//* look for s */
int mark::string(val s){Skip(); if(c+s.n>r->n) return 0;
if(!strncmp(&r->s[c],s.s,s.n)) {c+=s.n; return 1;} return 0;}
/*-----*/
/* int mark::string(val s){Skip(); mark e=here(*this,s);
if(!e.r) return 0; *this=e; return 1;} */
/*-----*//* if n=0, s = 0 or 1 or -> standard empty string, do not delete it */
void val::del(){int i; call*c; switch(n){
case _keyseq: case _unidfname: delete s; break;
case _call: for(i=(c=C)->n-1;i>=0;i--) c->arg[i].del(); delete c; break;
default: if(n>0 ?: magic()) delete s;} n=0; s=0;}
/*-----*/
/* char*Copytext(reg char*t,reg char*s,int n){reg char*u=t+n;
while(t<u) *t++=*s++; return u;} */
/*-----*//* fast move s[0:n-1]=t[0:n-1] */
char* Copytext(char*s,char*t,int n){
asm("pushl %esi"); asm("pushl %edi"); asm("cld");
asm("movl 8(%ebp),%edi"); asm("movl 12(%ebp),%esi"); asm("movl 16(%ebp),%ecx");
asm("rep"); asm("movsb"); asm("movl 8(%ebp),%eax");
asm("popl %edi"); asm("popl %esi");}
/*-----*/
char*copyof(char*s,int n/*=0*/){if(!n) n=strlen(s); if(!s?:!n) return "\000";
char*t=new char[n+1]; Copytext(t,s,n); t[n]=0; return t;}
/*-----*/
char*copyof(const val&s){if(!s.s?:s.n<=0) return "\000";
char*t=new char[s.n+1]; Copytext(t,s.s,s.n); t[s.n]=0; return t;}
/*-----*/
val named(val name){int i; Subr*s; char*w; macro*m; val f; var*v;
if(s=namedsubr(name)) return val(s); /* subr name */
for(m=macros;m;m=m->next) if(m->name) if(name==m->name) return val(m);
for(i=0;w=rsvword[i];i++) if(name==w) return val(i,_rsvword);/* reserved word */
for(i=0;i<255;i++) if(name==altnames[i]) return val(256+i,_char); /*****/
for(i=0;f=keynames[i],f.s;i++) {if(name==f.s) return val(f.n,_char);}
if(name.n==5) if(!strncmp(name.s,"ctrl",4)) return val(name.s[4]&31,_char);
for(i=0;w=funcname[i].name;i++) if(name==w) return val(&funcname[i]);
for(i=-2;w=keysort[i];i++) if(name==w) return val(-i,_type);
for(v=globvars;v;v=v->next) if(name==v->name) return val(v);
if(Record) for(v=Record->vars;v;v=v->next) if(name==v->name) return val(v);
return val(copyof(name),_unidfname);}
/*-----*/
val mark::label(){mark x=*this; Skip(); if(!is_alpha()) Bad("not a name");
int y=c; while(is_alnum()) ++*this; int z=c;
if(!thisch(':')) Bad("no ':' after label"); return named(val(r->s+y,z-y));}
/*-----*/
val mark::name(){mark x=*this; Skip(); if(!is_alpha()) Bad("not a name");
int y=c; while(is_alnum()) ++*this; return named(val(r->s+y,c-y));}
/*-----*/
val mark::number(){int C,i=0; mark x=*this; Skip();
if((C=**this-'0')<0?:C>9) Bad("not a number");
do {c++; i=10*i+C;} while((C=**this-'0')<0?0:C<=9); return val(i);}
/*-----*//* look for text in " " */
val mark::string(){int i,j,k,m=0,magic=0; char C,W[i=lwand(strsize)+1];
mark x=*this; for(i--;i>=0;i--) W[i]=0;
if(thisch('"')) for(j=0;;c++) {
if(magic) {m=1; magic=0; W[j>>3]|=128>>(j&7);}
if(j>=strsize-1) Bad("string too long");
switch(C=**this){
case '`': magic=1; break; /* next char is magic */
case '\\': c++; C=**this;
if(C<'0'?0:C<'8') {k=C-'0'; c++; C=**this; /* \ and 3 octal digits */
if(C<'0'?0:C<'8') {k=k*8+C-'0'; c++; C=**this;
if(C<'0'?0:C<'8') {k=k*8+C-'0'; c++; C=**this;}} c--;CW[j++]=k;}
else {if(!eol()) CW[j++]=**this; /* code char as itself */} break;
case '^': c++; CW[j++]=**this&31; break; /* control char */
case '"': CW[j]=0; c++; if(!m) return val(j?copyof(CW,j):"\000",j);
k=lwand(j); for(i=0;i<k;i++) CW[j+1+i]=W[i];
return val(copyof(CW,j+1+k),j|0x80000000); /* magic string */
case LF: Bad("eol in string");
default: CW[j++]=C;}}
Bad("not a string");}
/*-----*/
val mark::elem(){char C; mark x=*this; Skip();
val s=number(); if(s.n!=_bad) return s;
s=name(); if(s.n!=_bad) return s;
s=string(); if(s.n!=_bad) return s;
if(eol()) Bad("(part of) expression missing at eol");
if((C=**this)=='(') {c++; s=expr(); badq(s); if(thisch(')')) return s;
s.del(); Bad("rubbish or eol found instead of )");}
Bad(strncmp(s.s,"not a ",6)?s.s:"not an expression element");}
/*-----*/
typedef struct{char res,Lr,L,Rr,R; char*op; char pr; func*f; char*com;} op_use;
int npriosused;
char*Eq=" =="+1,*Ne=" !="+1,*Ge=" >="+1,*Le=" <="+1,*Gt=" >"+1,*Lt=" <"+1,
*Alc=" ="+1,*Add=" +"+1,*Sub=" -"+1,*Tim=" *"+1,*Div=" /"+1;
char*Ops[]={Alc,Add,Sub,Tim,Div,0}; char op_from_right[27]={[14]1};
op_use *PP[28]={0},ops[]={ /*** keep these entries in priority order ***/
{_int ,1,_int ,0,_int ,Alc,14,_allocate,0},
{_string,1,_string,0,_string,Alc,14,_allocate,0},
{_char ,1,_char ,0,_char ,Alc,14,_allocate,0},
{_int ,0,_string,0,_string,Eq, 7,_eq, 0},
{_int ,0,_string,0,_string,Ne, 7,_ne, 0},
{_int ,0,_int ,0,_int ,Eq, 7,_eq, 0},
{_int ,0,_int ,0,_int ,Ne, 7,_ne, 0},
{_int ,0,_int ,0,_int ,Ge, 6,_ge, 0},
{_int ,0,_int ,0,_int ,Le, 6,_le, 0},
{_int ,0,_int ,0,_int ,Gt, 6,_gt, 0},
{_int ,0,_int ,0,_int ,Lt, 6,_lt, 0},
{_int ,0,_int ,0,_int ,Add, 4,_plus, 0},
{_int ,0, 0 ,0,_int ,Add, 4,_same, 0},
{_int ,0,_int ,0,_int ,Sub, 4,_minus, 0},
{_int ,0, 0 ,0,_int ,Sub, 4,_neg, 0},
{_int ,0,_int ,0,_int ,Tim, 3,_times, 0},
{_int ,0,_int ,0,_int ,Div, 3,_divide, "rounds towards -inf; x/0 = 0"},
{0,0,0,0,0,0,27,0,0}};
enum{MINUSPR=4};
/**** and secure all func's & subr's against bad values in args ****/
/*-----*//* set up info tables re operators */
void set_up_PP(){op_use*B; int i=0,j=0; if(PP[0]) return;
for(B=ops;B->op;B++) {if(j!=B->pr) j=(PP[i++]=B)->pr; B->op[-1]=i-1;}
PP[npriosused=i]=B;}
/*-----*/
int display_op_uses(){int i,j=1; op_use*P; set_up_PP();
display("operator uses allowed in macros (# = must be a variable)",0,0,Green);
for(i=0;(P=&ops[i])->op;i++) {
if(P->L) pr(CW,"%s%t%T",P->Lr?"#":" ",P->L,9); else pr(CW,"%T",9);
pa(CW,"%-2s %s%t%T: returns %t%T: priority %1d %s",P->op,
P->Rr?"#":" ",P->R,11,P->res,28,npriosused-P->op[-1],P->com?:"");
if(j==gp_Rows-1) {display("(More)",j,0,Green); get_key(); j=0;}
display(CW,j++,0,Orange);}
return j;}
/*-----*/
char*mark::op(int p){mark x=*this; Skip();
char*s, a=**this, b=c+1>=r->n?CR:r->s[c+1]; op_use*B,*C=PP[p+1];
for(B=PP[p];B<C;B++) if(a==(s=B->op)[0]) if(!s[1]?1:b==s[1]) goto A;
err=*this; *this=x; return 0; A: c++; if(s[1]) c++; return s;}
/*-----*/
val callop(val L,char*op,val R){
int Lt=L.type(),Rt=R.type(),prio=op[-1]; op_use*B,*C=PP[prio+1]; val t,ar[3];
for(B=PP[prio];B<C;B++) if(op==B->op) if(Lt==B->L) if(Rt==B->R)
if(B->Lr?L.n==_ref:1) if(B->Rr?R.n==_ref:1) goto OK;
pr(CW,"type error in expr:");
if(L.n) pa(CW," %s %t", L.n==_ref?"ref":"nonref",Lt);
pa(CW, " %s %s %t",op,R.n==_ref?"ref":"nonref",Rt);
L.del(); R.del(); return val(CW,_bad);
OK: ar[0]=ff(B->f); ar[1]=L; ar[2]=R; t=call_n(3,B->res,B->pr,B->op,ar);
if(L.known_now()) if(R.known_now()) {val u=t(); t.del(); return u;} return t;}
/*-----*/
val mark::expr(int p/*=0*/){char *Op; val X,*Y,Z; int fr,i; mark x=*this;
set_up_PP(); Skip(); if(eol()) Bad("expression missing at eol");
if(p>=npriosused) return Call();
Op=op(p); X=expr(p+1); badq(X); if(Op) {X=callop(val(),Op,X); badq(X);}
if(fr=op_from_right[PP[p]->pr]) Y=&X.C->arg[2]; else Y=&X;
while(Op=op(p)) {Z=monexpr_upto(p+1); Badq(Z,X); *Y=callop(*Y,Op,Z);
if(fr) {Badq(*Y,X); Y=&Y->C->arg[2];}} return X;}
/*-----*/
val mark::monexpr_upto(int p){mark x=*this; Skip();
val X; char*op, a=**this, b=c+1>=r->n?CR:r->s[c+1]; op_use*B,*C=PP[p];
for(B=PP[0];B<C;B++) if(a==(op=B->op)[0]) if(!op[1]?1:b==op[1]) goto A;
err=*this; *this=x; return expr(p); A:
c++; if(op[1]) c++; X=monexpr_upto(p); badq(X); X=callop(val(),op,X); return X;}
/*-----*/
static var*declare(char*name,int type){
if(type==_label) return Record->vars=new var(name,type,0,Record->vars);
else if(Record)
return Record->vars=new var(name,type,Record->nvars++,Record->vars);
else return globvars=new var(name,type,nglobvars++,globvars);}
/*-----*/
int val::type(){int i; short*Z; switch(n) {
default: return magic()?_magic:notstring()?n:_string;
case _ref: return v->type;
case _subr: case _Subr: case _macro: return 0;
case _func: i=(Z=Funcinfo()->args)[0]; return i!=_adinf?i:Z[1];
case _Func: i=(Z=Fn ->args)[0]; return i!=_adinf?i:Z[1];
case _call: return C->type;}}
/*-----*/
int val::typ(){switch(n) {
default: return magic()?_magic:notstring()?n:_string;
case _ref: return v->type;}}
/*-----*/
int val::checktype(int typ){int m=type(); if(typ==m ?: typ==666) return 1;
switch(typ){
case _magic: return m==_string;
case _keyseq: return m==_char;
case _char: return m==_string;
case _label: if(m==_unidfname) {*this=named(s); /* already thus declared? */
if(n==_unidfname) {n=_ref; v=declare(copyof(s),_label);} return 1;}}
return 0;}
/*-----*/
val val::convto(int t){int f=typ(); val v; if(t==f) return *this; switch(f){
case _string: if(t==_magic?:t==_char) return *this;
case _char: if(t==_keyseq) return *this; break;}
pr(CW,"can't convert %t to %t",f,t); MOAN(CW); return val();}
/*-----*/
int val::known_now(){
switch(n){case _subr: case _macro: case _call: case _Subr:
case _func: case _Func: case _ref: return 0;} return 1;};
#define MoaN(s) ({err=*this; *this=x; Moan=s; goto BAD;})
/*-----*/
val mark::Call(){
val t,u,args[32],f,g,N,Arg[4],*z; int i=0,j,k,l,m,n,se; mark x=*this;
char*ac; call*C; mark q[33]; static short MA[]={_int,0}; jmp_buf*oldbad,failed;
short*a; Skip(); q[0]=*this; n=0; if(eol()) MoaN("matter missing at eol");
t=elem(); badq(t); args[0]=t; q[1]=*this; n=1;
if(thisch('(')) if(!thisch(')')) {
do {u=expr(); q[i=n+1]=*this; if(u.bad()) {Moan=u.s; goto BAD;}
if(n>31) {u.del(); MoaN("> 31 args");}
args[n++]=u;} while(thisch(','));
if(!thisch(')')) MoaN("rubbish after arg");}
switch(t.n) {
case _Subr: a=t.S->args-1;
Arg[1]=val(1,0); Arg[2]=val(); Arg[3]=val();
for(i=j=1;(k=a[j])?i<n:0;j++) if(args[i].checktype(k)) Arg[j]=args[i++];
if(i!=n) goto BADARG;
Arg[0]=kf(t.S->f); return val(call_n(4,0,0,t.S->name,Arg));
case _Func: a=t.Fn->args; if(se=(a[0]==_adinf)) a++;
for(i=j=1;i<n;i++) {
if(!a[j]) MoaN("too many args");
if(!args[i].checktype(a[j])) {a++; goto BADARG;}
if(a[j+1]!=_adinf) j++;}
k=a[0]; if(se) goto Z;
for(i=1;i<n;i++) if(!args[i].known_now()) goto Z; /* can I find value now?*/
oldbad=bad; bad=&failed; if(setjmp(*bad)) goto BADD;
t=t.Fn->f(n,args); for(i=0;i<n;i++) args[i].del(); bad=oldbad; return t;
BADD: err=x; bad=oldbad; return val(Moan,_bad);
/* case _func: k=Funcinfo()->args[0]; */
Z: return val(call_n(n,k,0,t.Fn->name,args));
case _macro: i=1; N=val(1,0); if(i<n) if(args[i].checktype(_int)) N=args[i++];
if(i!=n) {a=MA; goto BADARG;}
Arg[0]=kf(&obey); Arg[1]=N; Arg[2]=t; Arg[3]=val();
return val(call_n(4,0,0,"obey",Arg));
case _type: if(n!=1) {pr(CW,"'%t' should not have args",t.i); i=1; MoaN(CW);}
return t;
default: if(n==1) return t; /* has no args so isn't a call */
pr(CW,"a %t can't be a call base",t.n); i=0; MoaN(CW);}
BADARG:
if(a[1]) {pr(CW,"args %sshould be:",t.n==_Func?"":"(can be omitted) ");
for(l=1;k=a[l];l++) {if(k==_adinf) {pa(CW,"s"); /* plural */ break;}
else pa(CW," %t",k);}}
else pr(CW,"should have no args");
ac=CW+strlen(CW);
if(n) {pa(CW," args are:"); for(l=1;l<n;l++) pa(CW," %t",args[l].type());}
else pa(CW," has no args");
*ac=0; Display=CW; Moan=ac+1;
BAD: err=q[i]; for(i=0;i<n;i++) args[i].del(); *this=x; return val(Moan,_bad);;}
#define Bad(s) ({err=*this; *this=x; return val(s,_bad);})
/*-----*/
char*not_a_decl="not a decl";
val mark::decl(){val args[32]; int i=0,n=0; mark q[33],x=*this; Skip();
if(eol()) return val(); q[0]=*this; args[0]=name(); q[1]=*this; n=1;
if(args[0].n!=_type?: (Skip(), !is_alnum())) {Moan=not_a_decl; goto BAD;}
do {val u=name(); q[i=n+1]=*this;
if(u.bad()) {Moan="bad name in decl"; goto BAD;}
if(n>31) {u.del(); Moan="declaration has > 31 args"; goto BAD;}
if(u.n!=_unidfname) {
u.print(Moan=CW); pa(CW," is already a %t",u.typ()); u.del(); goto BAD;}
args[n++]=u;} while(thisch(','));
return val(call_n(n,0,0,keysort[-args[0].i],args));
BAD: err=q[i]; for(i=0;i<n;i++) args[i].del(); *this=x; return val(Moan,_bad);;}
/*-----*/
call*call_n(int n,int type,int pr,char*name,val*a){
call*c=(call*)myalloc(sizeof(call)+(n-1)*sizeof(val));
c->n=n; c->type=type; c->pr=pr; c->name=name;
int i; val*b=c->arg; for(i=0;i<n;i++) b[i]=a[i]; return c;}
/*-----*/
KF(_idle){}
/*-----*/
static macstep*jump=0;
KF(go_to){if(T1.n==_ref) if(T1.v->type==_label) jump=(macstep*)(T1.v->offset);}
/*-----*/
KF(If){} /* 'if' is handled elsewhere */
/*-----*/
subr*subrcalled(val V){if(V.n==_call) V=V.C->arg[0]; switch(V.n){
case _subr: return V.f; case _Subr: return V.S->f; default: return 0;}}
/*----- is val a char or the name of a (char or special key)? */
int val::charval(){
if(n==1) return s[0]; /* string with one char */
if(n==_char) return i;
if(n<1) return -1; int j;
if(n<4) for(j=0;j<255;j++) if(*this==altshortnames[j]) return j+256; return -1;}
/*-----*/
void val::expandkeyseq(char*K,int plain/*=0*/){int e,j,k,l,p; char *x,*S=s,a[3];
if(n==_char) if(S=a,i&~255) {a[0]=3; a[1]=0; a[2]=i&255;} else {a[0]=2; a[1]=i;}
else if(n!=_keyseq) MOAN("BUG: expandkeyseq bad arg"); p=(byte)S[0];
if(plain) {for(e=1,K[0]=0;e<p;e++) if(S[e]) {
strcat(K,keyname((byte)S[e],e>1?!S[e-1]:0)); strcat(K," ");}
e=strlen(K); K[e-1]=0; return;}
strcpy(K,"keyseq("); k=6;
for(e=1;e<p;e++) if(S[e]) {x=keyname((byte)S[e],e>1?!S[e-1]:0);
if(x[1]) {strcat(K,x); k=strlen(K);}
else {j=(byte)*x;
K[k++]='"'; if(j=='"'?:j=='\\') K[k++]='\\'; K[k++]=j; K[k++]='"';}
K[k++]=','; K[k]=0;}
K[k-1]=')'; K[k]=0;}
/*-----*/
#undef Bad
/*----- translate buffer to macro */
void translate(buffer*BB){int i,j,t,n,nargs; buffer*BBB=B; macro*m;
val *arg,*K=0,N,S,T,T1,T2,U,V,W; Record=0; char*ac,spec; mark par,x,y; subr*Z;
jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) goto BAD;
/* if(record) {Moan="can't read in macros while recording a macro"; goto BAD;}*/
T=val(); if(B!=BB) BB->go_to();
for(i=0;i<basemi.nvars;i++) basemi.stack[i].del();
delete globvars; delete basemi.stack;
globvars=0; basemi.stack=0; nglobvars=basemi.nvars=0; par=mark(B->text.next,0);
NEXTINSTR: B->dot=par; B->dotcc=-1; B->display();
x=par; par.Skip(); err=par; spec=0;
if(par.eof()) {if(B!=BBB) BBB->go_to(); bad=oldbad; Moan=0; return;} y=par;
if(Record) if(T=par.label(), T.n!=_bad) {var*v;
if(T.n==_unidfname) {T.n=_ref; T.v=declare(copyof(T.s),_label);}
if(T.n==_ref) {*Record+=new macstep(T);
if(T.v->type==_label) {
if(T.v->offset) {Moan="label defined twice"; goto BAD;}
T.v->offset=(long int)Record->last; goto NEXTINSTR;}
else {pr(Moan=CW,"this label is already declared as a %t",T.v->type);
goto BAD;}}
else {pr(Moan=CW,"a %t can't be a label",T.n); goto BAD;}}
T=par.decl(); if(T.n!=_bad ?: T.s!=not_a_decl) goto A; Moan=0; par=y;
spec=par.thisch('#'); T=par.expr(); if(T.n==_bad) {Moan=T.s; goto BAD;}
A: if(!par.thisch(';')) {
err=par; par=x; T.del(); Moan="rubbish after command"; goto BAD;}
if(spec) switch(T.n) {
default: pr(Moan=CW,"%t after '#'",T.typ()); goto BAD;
case _bad: Moan=T.s; goto BAD;
case _subr: Z=T.f; goto Y;
case _Subr: Z=T.S->f; Y: N=val(1,0); T1=val(); T2=val(); goto Z;
case _call: switch(U=(arg=T.C->arg)[0], Z=U.f, U.n) {
case _Subr: U=kf(Z=U.S->f);
case _subr: N=arg[1]; T1=arg[2]; T2=arg[3];
Z: if(Z==&unbindkey) {if(T1.n) if(K=&T1.keyseq()) K->unbind(); K=0;}
else if(Z==&buffer_) {if(T1.n) buffer_(N,T1,T2); BB->go_to();}
else if(Z==&beginmacro) { /* new macro named T1 bound to key T2 */
if(T2.n) {if(Moan=(K=&T2.keyseq())->moanifbound(T2,0)) goto BAD;
(Record=new macro())->bound=keyseq.copy();
if(T1.n>0) namemacro(val(1,0),T1,val(Record)); *K=*Record;}
else {K=0; Record=new macro();
if(T1.n>0) namemacro(val(1,0),T1,val(Record));
else {for(m=macros;m;m=m->next) if(m->name==CMN)
if(m->bound.n) m->name=0;
else if(Macro!=record) delete m;
Record->name=CMN; record=0; Macro=Record;}}}
/* unnamed & unbound macro terminates and replaces current macro */
else if(Z==&endmacro) {Record=0; K=0; break;}
else {Moan="wrong subroutine after '#'"; goto BAD;} break;
default: pr(Moan=CW,"call of a %t after '#'",U.n); goto BAD;}
break;}
else switch(T.n) {
case _bad: Moan=T.s; goto BAD;
case _rsvword: pr(Moan=CW,"'%s' used wrongly",rsvword[T.i]); goto BAD;
case _unidfname: pr(Moan=CW,"'%s' not known",T.s); goto BAD;
default: pr(Moan=CW,"this is a %t",T.typ()); goto BAD;
case _Subr: case _subr: case _Func: case _macro: *Record+=new macstep(T); break;
case _call: U=(arg=T.C->arg)[0];
if(U.n!=_type)if(!Record){Moan="instruction not in a macro body"; goto BAD;}
switch(U.n) {
case _type: switch(U.i) {
default: Moan="can't declare this type of variable yet"; goto BAD;
case _int: case _string: for(i=1;i<T.C->n;i++) {W=T.C->arg[i];
if(W.n==_unidfname) W=named(val(W.s)); if(W.n!=_unidfname) {
W.print(Moan=CW); pa(CW," is already a %t",W.typ()); goto BAD;}
declare(W.s,U.i);}} break;
case _Subr: case _subr: *Record+=new macstep(U,arg[1],arg[2],arg[3]);
break;
case _macro: *Record+=new macstep(U,arg[1]); break;
/* case _macro: *Record+=new macstep(kf(&obey),arg[1],U); break;*/
case _Func: case _func: *Record+=new macstep(T); T=val(); break;
default: pr(Moan=CW,"call base is a %t",U.typ()); goto BAD;}}
goto NEXTINSTR;
BAD: bad=oldbad; BB->go_to(); T.del(); B->dotcc=-1; err.c<?=err.r->n;B->dot=err;
if(Record) {if(K) *K=val(); delete Record; Record=0;} MOAN(Moan);}
/* unbind & delete incomplete macro */
/*-----*/
#define __(name,Name,args) {&name,0,0,0,Name,args}
val __idle(int i,val*v){return val();}
Func anonfunc=__(__idle,"###",0); Subr anonsubr=__(_idle,"@@@",0);
/*-----*/
Subr*val::Subrinfo(){reg int i; reg char*s;
for(i=0;s=subrname[i].name;i++) if(f==subrname[i].f) return &subrname[i];
return &anonsubr;}
/*-----*/
Func*val::Funcinfo(){reg int i; reg char*s;
for(i=0;s=funcname[i].name;i++) if(fn==funcname[i].f) return &funcname[i];
return &anonfunc;}
/*-----*/
char*val::Subrname(){return f ?Subrinfo()->name:"<<unknown subroutine>>";}
char*val::Funcname(){return fn?Funcinfo()->name:"<<unknown function>>";}
/*-----*/
char*chname(int c){static char ct[3]="^ ",sc[3]="\\ ",cc[2]=" ",cs[5]="\\000";
c&=255; if(c>=128) {pr(cs,"\\%03o",c); return cs;}
if(specialchars>>c) {sc[1]=c; return sc;}
if(c<32) {ct[1]=c+64; return ct;} cc[0]=c; return cc;}
/*----- if ')' last before B->dot, replace it with ',', else insert '(' */
void start_an_arg(){int i=!B->dot.c?0:B->dot.r->s[B->dot.c-1]==')';
if(i) B->dot.bs(); *B+=i?',':'(';}
/*-----*/
val val::copy(){switch(n){
default: if(magic()) {int i=n&0x7fffffff; return val(copyof(s,i+1+lwand(i)),n);}
if(n<=0) return *this; return val(copyof(s,n),n);
case _keyseq: return val(copyof(s,(byte)s[0]),n);
case _unidfname: return val(copyof(s,0),n);}}
/*-----*/
void prvars(var*V){int i,j; var*v; int Nt[_typeend-_typebeg],*nt=Nt-_typebeg;
for(i=_typebeg;i<_typeend;i++) nt[i]=0;
for(j=0,v=V;v;v=v->next) {nt[v->type]++; j++;}
if(j) for(i=_typebeg;i<_typeend;i++) if(nt[i]) if(i!=_label) {
pb("%t ",i); for(v=V;v;v=v->next) if(v->type==i) pb("%s,",v->name);
B->dot.bs(); *B+=";\n";}}
/*-----*/
void macro::print(){int i; macstep *A,*C; char*N=name?:CMN; *B+="#beginmacro";
if(bound.n) if(N!=CMN) pb("(%S,%K)",N,&bound); else pb("(%K)",&bound);
else if(N!=CMN) pb("(%S)",N); *B+=';'; newline(); prvars(vars);
for(A=text;A;A=A->next) A->print(); *B+="#endmacro;\n";}
/*-----*/
void prsubrargs(val f,val N,val T1,val T2){f.print();
if(N.n ?:(uns int)N.s >=4096) {start_an_arg(); N.print(); *B+=')';}
if(T1.n?:(uns int)T1.s>=4096) {start_an_arg(); T1.print(); *B+=')';}
if(T2.n?:(uns int)T2.s>=4096) {start_an_arg(); T2.print(); *B+=')';}}
/*-----*/
void macstep::print(){int i,j,n=0; char *s; val v; switch(f.n){
case _Subr: if(f.S->f==&_idle) return; break;
case _subr: if(f.f==&_idle) return; break;
case _macro: if(f.m->name) break; v=kf(obey);
macstep(v,N,f.m->bound,val()).print(); return;
default: if(!f.magic()) if(f.notstring()) break;
case _char: case _keyseq: case _int: pb("*** "); break;
case _rsvword: pb("*** reserved: "); break;
case _ref: pb("%s: ",f.v->name); return;}
prsubrargs(f,N,T1,T2); *B+=';'; newline();}
/*-----*/
void val::print(char*Z/*=(char*)_buffer*/,int pr/*=1000*/){
int j,k,p; val*a; char*z;
if(this) switch(n){
default: if((j=type())<=0) p_r(Z,"<<<%t>>>",j); else p_r(Z,"%v",this); break;
case 0: if(!s) p_r(Z,"%s","<<<null>>>"); else p_r(Z,"\"\""); break;
case _type: p_r(Z,keysort[-i]); break;
case _keyseq: p_r(Z,"%K",this); break;
case _Subr: p_r(Z,S->name); break;
case _subr: p_r(Z,Subrname()); break;
case _Func: p_r(Z,Fn->name); break;
case _func: p_r(Z,Funcname()); break;
case _macro: if(m->name) p_r(Z,m->name);
else if(m->bound.n) p_r(Z,"%K",&m->bound);
else p_r(Z,"<<<unnamed unbound macro>>>"); break;
case _char: if(i<256) p_r(Z,"\"%s\"",chname(i));
else if(z=altnames[i&255]) p_r(Z,"%s",z);
else p_r(Z,"<<<char 0 %1d>>>",i&255); break;
case _buffer: p_r(Z,"<<<buffer \"%s\">>>",b->name); break;
case _int: z="%1d"; if(i<0) if(pr<=MINUSPR) z="(%1d)"; p_r(Z,z,i); break;
case _rsvword: p_r(Z,"%s",rsvword[i]); break;
case _unidfname: p_r(Z,"<<<unknown word: %s>>>",s); break;
case _bad: p_r(Z,"<<<bad parsing: %s>>>",s); break;
case _call: if(!C) break; p=C->n; a=C->arg; if(!a) break;
if(a[0].n==_func) {if(pr) goto EXPR; else p_r(Z,C->name);}
else if(a[0].n==_subr) {prsubrargs(a[0],a[1],a[2],a[3]); break;}
else a[0].print();
if(p<2) break;
for(j=1;j<p;j++) {start_an_arg(); a[j].print(); p_r(Z,")");} break;
EXPR: p=C->pr; j=1; k=0;
if(op_from_right[p]) {j=0; k=1;} if(pr<=p) p_r(Z,"(");
if(a[1].n?:a[1].i) a[1].print(Z,p+j); p_r(Z,C->name); a[2].print(Z,p+k);
if(pr<=p) p_r(Z,")"); break;
case _ref: p_r(Z,v->name); break;}}
/*-----*/
void macstep::del(){f.del(); N.del(); T1.del(); T2.del(); clear();}
/*----- remove pointers to buffers & macros which are being deleted */
void del_every(val f,keyarray&ka){int i,n=ka.n; val*F=ka.a;
for(i=0;i<n;i++,F++) if(F->n==_keyarray) {del_every(f,*F->k); return;}
else if(f==*F) F->n=F->i=0;}
void del_every(val f,val&m){val*a; int i,n; /* look in macro args */
if(m.n==_call) {a=m.C->arg; n=m.C->n; for(i=0;i<n;i++) del_every(f,*a++);}
else if(f==m) m.n=m.i=0;}
void del_every(val f,macstep*M){for(;M;M=M->next){del_every(f,M->f);
del_every(f,M->N); del_every(f,M->T1); del_every(f,M->T2);}}
void del_every(val f){macro*m; for(m=macros;m;m=m->next) del_every(f,m->text);
del_every(f,&thisstep); del_every(f,&laststep); del_every(f,keys);}
/*-----*/
macro::~macro(){macro**I; del_every(val(this));
for(I=&(macros);*I;I=&((*I)->next)) if(*I==(this)) {*I=(*I)->next; break;}
empty();};
/*----- delete all macsteps in macro */
void macro::empty(){macstep *A,*B; if(bound.n) delete bound.s; delete vars;
if(name!=CMN) delete name; for(B=text;A=B;B=A->next) delete A; text=last=0;}
/*----- macro+=macstep : append macstep to macro */
void macro::operator+=(macstep*m){if(play?:!this) return;
if(last) last->next=m; else text=m; (last=m)->next=0;}
/*----- tidy macro */ /* chain up insert/overlay char macsteps */
void macro::tidy(){int i,n; subr*u; macstep*M,*N,*P; char*s; if(!text) return;
for(M=text;M;M=M->next) if(M->T1.n==_char) if(M->N.i==1)
if((u=M->f.f)==&insert?:u==&overlay?:u==&nomove){/* chain chars into string */
for(n=1,N=(P=M)->next;;P=N,N=N->next,n++)
if(!N ?: N->T1.n!=_char ?: N->f.f!=u ?: N->N.i!=1) break;
if(N) if(N->f.f==&repeat) n--; if(n<=1) continue;
s=new char[n+1]; s[n]=0; s[0]=M->T1.i;
for(i=1,N=M->next;i<n;) {s[i++]=N->T1.i; P=N->next; delete N; N=P;};
M->T1=val(s,n); M->next=N;}
last=0; for(M=text;M;M=M->next) if(M) last=M;}
/*-----*/
macro *record=0,*Macro=0; int macdepth; macstep _lazy(kf(&_idle));
/*-----*/
static void G(){int i; if(!basemi.stack) {basemi.stack=new val[nglobvars];
for(i=0;i<nglobvars;i++) basemi.stack[i]=val();}}
/*-----*/
void macro::operator()(val N/*=val(1,0)*/,int r/*=0*/){int nn=N.n==_int?N.i:1;
val *p; int i,n,b=0; macstep*A; macrinfo MI,*oldmi=mi; mi=&MI; G();
jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) {b=1; goto BAD;}
if(n=nvars) {p=mi->stack=new val[mi->nvars=n]; for(i=0;i<n;i++) p[i]=val();}
if(macdepth++>16) {Moan="macro calls >16 deep"; goto BAD;}
for(i=0;jump=0,Breakin()?0:i<nn;i++)
for(A=text,MI.prevstep=&_lazy;A;A=jump?:A->next){
MI.rec=1; jump=0; (*A)(); mi=&MI;
if(MI.rec) MI.prevstep=A; if(Breakin()) goto BAD;}
BAD: for(i=0;i<mi->nvars;i++) mi->stack[i].del();
macdepth--; mi=oldmi; bad=oldbad; if(b) MOAN(Moan);}
/*-----*//*** be careful in case a loose macstep is a &repeat ***/
void macstep::operator()(int p/*=1*/){int i; subr*S;
prevobtype=obtype; obtype=ob_other; G();
switch(f.n) {default: break;
case _func: case _Func: case _call: for(i=0;i<p;i++) f(); break;
case _Subr: S=f.S->f; goto SS; case _subr: S=f.f;
SS: if(S==If) for(i=0;i<p;i++) N().i?T1():T2();
else for(i=0;i<p;i++) S(N(),T1(),T2()); break;
case _macro: for(i=0;Breakin()?0:i<p;i++) (*f.m)(N);} B->dotcc=-1;}
/*-----*/
val val::operator()(){int j,N; val *V,*arg,*lhs; subr*sub; macro*M; G();
switch(n){default: return*this;
case _macro: (*m)(); return val();
case _subr: (*f)(val(1,0),val(),val()); return val();
case _Subr: (*S->f)(val(1,0),val(),val()); return val();
case _Func: return Fn->f(1,this);
case _func: return fn(1,this);
case _ref: if(v->type==_label) return*this;
lhs=refaddr(*this); return lhs?*lhs:val();
case _call:;}
switch((arg=C->arg)[0].n){
default: pr(CW,"tried to call a %t",arg[0].type()); MOAN(CW);
case _macro: M=arg[0].m; (*M)(arg[1]); break;
case _subr: sub=arg[0].f; goto F;
case _Subr: sub=arg[0].S->f;
F: if(sub==If) arg[1]().i?arg[2]():arg[3]();
else (*sub)(arg[1](),arg[2](),arg[3]()); break;
case _Func: return arg[0].Fn->f(C->n,arg);
case _func: return arg[0].fn (C->n,arg);
case _type: pr(CW,"illegal call of '%t'",i); MOAN(CW);}
return val();}
/*-----*/
macro*macro_menu() {int i,n; macro*mac,*M; for(n=0,M=macros;M;M=M->next) n++;
if(play) MOAN("BUG: obeyed macromenu from macro"); if(!n) return 0;
mousestate ms; ms=Jerry; Jerry.mc=1; Jerry.range(n,80); Jerry.move(0,0);
int j=0,k,m,w=gp_Rows-2,c=0; char*s; macro*macs[n];
for(i=0,M=macros;M;M=M->next,i++) macs[i]=M;
E: k=w/2; k=j<w?0:((j-w/4)/k)*k; display("MACROS DEFINED",0,0,Magenta+8);
for(i=k;i<n;i++) {if(i-k>=w) break; s=macs[i]->name; *CW=0;
if(macs[i]->bound.n) pr(CW," %K",&macs[i]->bound); if(s) pa(CW," `%s'",s);
if(!*CW) pr(CW,CMN); CW[gp_Cols]=0; display(CW,i-k+1,0,Magenta+8);}
for(i=(w<?n)-1;i>=n-k;i--) display(" ",i,0,Magenta+8);
display("(\030\031 move, RET chooses, alt_end quits)", (w<?n)+1,0,Magenta+8);
A: i=k; k=w/2; k=j<w?0:((j-w/4)/k)*k; if(i!=k) goto E; m=j-k+1;
scr(m,0)=sch(2,White); switch(c=getkey()) {
case -mousemove: j=Jerry.y; break;
case -downarrow: j=(j+1 )%n; break;
case -uparrow: j=(j-1+n)%n; break;
case -alt_end: case -mbutton: case -rbutton: Jerry=ms; return 0;
case CR: case -lbutton: Jerry=ms; return macs[j];}
scr(m,0)=sch(' ',White); if(c!=-mousemove) Jerry.move(0,j); goto A;}
/*-----*/
/* enum {_unbound=0,_subr=-1,_macro=-2,_char=-3,_keyarray=-4,_buffer=-5,_int=-6,
_keyseq=-7,_rsvword=-8,_unidfname=-9,_call=-10,_bad=-11,_Subr=-12,_func=-13,
_Func=-14}; */
/*-----*/
val*refaddr(val L){int i; if(L.n!=_ref) return 0;
return &(((i=L.v->offset)&0x80000000)?basemi:*mi)[i&0x7fffffff];}
/*-----*/
FN(_allocate){val*lhs=refaddr(arg[1]),x=arg[2]().copy();
if(lhs) {lhs->del(); *lhs=x;} return x;}
/*-----*/
FN(_andthen){if(macdepth++>16) MOAN("macro calls >16 deep");
macstep prev(_lazy); int i; macrinfo MI,*oldmi=mi; mi=&MI; mi->prevstep=&prev;
for(i=1;i<n;i++) {MI.rec=1; arg[i](); mi=&MI; if(MI.rec) prev.f=arg[i];}
macdepth--; mi=oldmi; return val();}
/*-----*/
int byteq(char*a,char*b,int n){reg byte *s=(byte*)a,*t=(byte*)b,*u=s+n;
while(s<u) if(*s++!=*t++) return 0; return 1;}
int streq(val a,val b){if(a.n!=b.n?:a.n<0) return 0; return byteq(a.s,b.s,a.n);}
/*-----*//* treat val() = undefined as 0 */
#define EA val a=arg[1](),b=arg[2]();
FN(_eq ){EA; return a.n==_int ? a.i == b.i : streq(a,b);}
FN(_ne ){EA; return a.n==_int ? a.i != b.i : !streq(a,b);}
FN(_ge ){return arg[1]().i >= arg[2]().i;}
FN(_le ){return arg[1]().i <= arg[2]().i;}
FN(_gt ){return arg[1]().i > arg[2]().i;}
FN(_lt ){return arg[1]().i < arg[2]().i;}
FN(_plus ){return arg[1]().i + arg[2]().i;}
FN(_minus ){return arg[1]().i - arg[2]().i;}
FN(_times ){return arg[1]().i * arg[2]().i;}
FN(_divide){val b=arg[2](); return b.i?arg[1]().i/b.i:0;} /* x/0 = 0 here */
FN(_same ){return arg[2]().i;}
FN(_neg ){return - arg[2]().i;}
/*-----*/
FN(currentbuffer){return val(B->name?:".no file.");}
/*-----*/
FN(keyseq_){int i,j,m; for(i=j=1;i<n;i++) {
if((m=arg[i]().charval())<0) MOAN("bad keyseq arg");
if(m>255) keyseqc[j++]=0; keyseqc[j++]=m&255;
if(j>61) MOAN("keyseq with too many args");}
keyseqc[0]=j; return keyseq.copy();}
/*-----*/
/* In run time, FN's return value, or val() (= void)), or val(<string>,_bad).
In compile time, ditto, or val(type,_bad) = "delivers that type but I can't
find its value now". val(_bad,_bad) = "ditto but I can't tell now what type
it delivers". val(1,_bad) = "ditto but delivers string". */